package com.yxy.chatserver.util;

import com.yxy.chatserver.properties.JwtProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * JWT工具类
 */
@Component
@Slf4j
public class JwtUtil {

    @Autowired
    private JwtProperties jwtProperties;

    // 生成安全的密钥
    private SecretKey secretKey;

    // Token 签发者
    private final String JWT_ISSUER = "chat-server";

    // 构造器注入
    @Autowired
    public JwtUtil(JwtProperties jwtProperties) {
        this.jwtProperties = jwtProperties;
        // 验证配置
        if (jwtProperties.getUserSecretKey() == null || jwtProperties.getUserSecretKey().trim().isEmpty()) {
            throw new IllegalArgumentException("JWT密钥不能为空");
        }
        this.secretKey = Keys.hmacShaKeyFor(jwtProperties.getUserSecretKey().getBytes());
        log.info("JWT工具类初始化完成");
    }

    /**
     * 生成JWT令牌
     */
    public String generateToken(String username, Long userId) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("username", username);

        return Jwts.builder()
                .claims(claims)                    // 自定义声明
                .subject(username)                 // 主题（通常是用户名）
                .issuer(JWT_ISSUER)               // 签发者
                .issuedAt(new Date())              // 签发时间
                .expiration(new Date(System.currentTimeMillis() + jwtProperties.getUserExpire())) // 过期时间
                .signWith(secretKey)              // 签名密钥
                .compact();
    }

    /**
     * 生成刷新令牌（有效期更长）
     */
    public String generateRefreshToken(String username, Long userId) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("username", username);
        claims.put("type", "refresh");  // 标记为刷新令牌

        return Jwts.builder()
                .claims(claims)
                .subject(username)
                .issuer(JWT_ISSUER)
                .issuedAt(new Date())
                .expiration(new Date(System.currentTimeMillis() + jwtProperties.getUserExpire() * 7)) // 7天
                .signWith(secretKey)
                .compact();
    }

    /**
     * 从令牌中解析声明
     */
    public Claims extractAllClaims(String token) {
        try {
            return Jwts.parser()
                    .verifyWith(secretKey)        // 验证密钥
                    .build()
                    .parseSignedClaims(token)      // 解析签名令牌
                    .getPayload();                 // 获取声明内容
        } catch (JwtException e) {
            log.error("JWT解析失败: {}", e.getMessage());
            throw new RuntimeException("令牌无效");
        }
    }

    /**
     * 从令牌中获取指定声明
     */
    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    /**
     * 从令牌中获取用户名（jwt中的subject部分）
     */
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    /**
     * 从令牌中获取用户ID
     */
    public Long extractUserId(String token) {
        return extractClaim(token, claims -> claims.get("userId", Long.class));
    }

    /**
     * 从令牌中获取过期时间
     */
    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    /**
     * 验证令牌是否有效
     */
    public boolean validateToken(String token, String username) {
        try {
            final String tokenUsername = extractUsername(token);
            return (username.equals(tokenUsername) && !isTokenExpired(token));
        } catch (Exception e) {
            log.error("令牌验证失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 验证令牌是否过期
     */
    private boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    /**
     * 检查令牌是否即将过期（在30分钟内过期）
     */
    public boolean isTokenAboutToExpire(String token) {
        final Date expiration = extractExpiration(token);
        // 检查是否在30分钟内过期
        return expiration.before(new Date(System.currentTimeMillis() + 30 * 60 * 1000));
    }

    /**
     * 刷新令牌（生成新令牌）
     */
    public String refreshToken(String oldToken) {
        try {
            Claims claims = extractAllClaims(oldToken);
            String username = claims.getSubject();
            Long userId = claims.get("userId", Long.class);

            // 生成新令牌
            return generateToken(username, userId);
        } catch (Exception e) {
            log.error("令牌刷新失败: {}", e.getMessage());
            throw new RuntimeException("令牌刷新失败");
        }
    }

    /**
     * 验证刷新令牌
     */
    public boolean validateRefreshToken(String token) {
        try {
            Claims claims = extractAllClaims(token);
            String tokenType = claims.get("type", String.class);
            return "refresh".equals(tokenType) && !isTokenExpired(token);
        } catch (Exception e) {
            return false;
        }
    }
}
